<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Reflect</title>
</head>
<body>
<script type="text/javascript">
    {
        /**
         * 1、将Object对象的一些明显属于语言内部的方法（比如Object.defineProperty），放到Reflect对象上。现阶段，某些方法同时在Object和Reflect对象上部署，未来的新方法将只部署在Reflect对象上。
         * 2、修改某些Object方法的返回结果，让其变得更合理。比如，Object.defineProperty(obj, name, desc)在无法定义属性时，会抛出一个错误，而Reflect.defineProperty(obj, name, desc)则会返回false。
         * 3、让Object操作都变成函数行为。某些Object操作是命令式，比如name in obj和delete obj[name]，而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。
         * 4、Reflect对象的方法与Proxy对象的方法一一对应，只要是Proxy对象的方法，就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法，完成默认行为，作为修改行为的基础。也就是说，不管Proxy怎么修改默认行为，你总可以在Reflect上获取默认行为。
         */
        /*老写法*/
        /*try {
            Object.defineProperty(target, property, attributes);
            // success
        } catch (e) {
            // failure
        }*/
        console.log('assign' in Object); // true

        /*新写法*/
        /*if (Reflect.defineProperty(target, property, attributes)) {
            // success
        } else {
            // failure
        }*/
        console.log(Reflect.has(Object, 'assign')); // true

        /*Proxy(target, {
            set: function (target, name, value, receiver) {
                var success = Reflect.set(target, name, value, receiver);
                if (success) {
                    log('property ' + name + ' on ' + target + ' set to ' + value);
                }
                return success;
            }
        });*/
        /*上面代码中，Proxy方法拦截target对象的属性赋值行为。它采用Reflect.set方法将值赋值给对象的属性，然后再部署额外的功能*/

        /*下面是另一个例子*/
        /*var loggedObj = new Proxy(obj, {
            get(target, name) {
                console.log('get', target, name);
                return Reflect.get(target, name);
            },
            deleteProperty(target, name) {
                console.log('delete' + name);
                return Reflect.deleteProperty(target, name);
            },
            has(target, name) {
                console.log('has' + name);
                return Reflect.has(target, name);
            }
        });*/
        /*上面代码中，每一个Proxy对象的拦截操作(get、delete、has)，内部都调用对应的Reflect方法，保证原生行为能够正常执行。添加的工作，就是将每一个操作输出一行日志*/
    }
    {
        /**
         * Reflect对象的方法
         * Reflect.apply(target,thisArg,args)：等同于Function.prototype.apply.call(fun,thisArg,args)
         * Reflect.construct(target,args)：等同于new target(...args)，这提供了一种不使用new，来调用构造函数的方法
         * Reflect.get(target,name,receiver)：查找并返回target对象的name属性，如果没有该属性，则返回undefined。如果name属性部署了读取函数，则读取函数的this绑定receiver
         * Reflect.set(target,name,value,receiver)：设置target对象的name属性等于value。如果name属性设置了赋值函数，则赋值函数的this绑定receiver
         * Reflect.defineProperty(target,name,desc)
         * Reflect.deleteProperty(target,name)：等同于delete obj[name]
         * Reflect.has(target,name)：等同于name in obj
         * Reflect.ownKeys(target)
         * Reflect.isExtensible(target)
         * Reflect.preventExtensions(target)
         * Reflect.getOwnPropertyDescriptor(target, name)
         * Reflect.getPrototypeOf(target)：读取对象的__proto__属性，对应Object.getPrototypeOf(obj)
         * Reflect.setPrototypeOf(target, prototype)：设置对象的__proto__属性，对应Object.setPrototypeOf(obj, newProto)
         */
        /*上面这些方法的作用，大部分与Object对象的同名方法的作用都是相同的，而且它与Proxy对象的方法是一一对应的。下面是对其中几个方法的解释*/

        /*1、Reflect.get(target, name, receiver)：查找并返回target对象的name属性，如果没有该属性，则返回undefined。如果name属性部署了读取函数，则读取函数的this绑定receiver*/
        let obj = {
            get foo() {
                return this.bar();
            },
            bar: function () {
                console.log(23333);
            }
        };
        // console.log(Reflect.get(obj, "foo", wrapper));
    }
    {
        /**使用 Proxy 实现观察者模式*/
        /*观察者模式(Observer mode)指的是函数自动观察数据对象，一旦对象有变化，函数就会自动执行*/
        const queuedObservers = new Set();
        const observe = fn => queuedObservers.add(fn);
        const observable = obj => new Proxy(obj, {set});

        function set(target, key, value, receiver) {
            const result = Reflect.set(target, key, value, receiver);
            queuedObservers.forEach(observer => observer());
            return result;
        }

        const person = observable({
            name: '张三',
            age: 20
        });

        function print() {
            console.log(`${person.name}, ${person.age}`) // 李四, 20
        }

        observe(print);
        person.name = '李四'
        /*上面代码中，先定义了一个Set集合，所有观察者函数都放进这个集合。然后，observable函数返回原始对象的代理，拦截赋值操作。拦截函数set之中，会自动执行所有观察者*/
    }
</script>
</body>
</html>